/**
 * 安全中间件
 * Security Middleware
 */

const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
const xss = require('xss');
const validator = require('validator');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const crypto = require('crypto');
const logger = require('../utils/logger');

/**
 * 请求频率限制配置
 */
const rateLimitConfig = {
  // 通用API限制
  general: rateLimit({
    windowMs: 15 * 60 * 1000, // 15分钟
    max: 100, // 每个IP最多100个请求
    message: {
      success: false,
      message: '请求过于频繁，请稍后再试'
    },
    standardHeaders: true,
    legacyHeaders: false,
    handler: (req, res) => {
      logger.warn(`Rate limit exceeded for IP: ${req.ip}, Path: ${req.path}`);
      res.status(429).json({
        success: false,
        message: '请求过于频繁，请稍后再试'
      });
    }
  }),

  // 登录限制
  auth: rateLimit({
    windowMs: 15 * 60 * 1000, // 15分钟
    max: 5, // 每个IP最多5次登录尝试
    message: {
      success: false,
      message: '登录尝试过于频繁，请15分钟后再试'
    },
    skipSuccessfulRequests: true,
    handler: (req, res) => {
      logger.warn(`Auth rate limit exceeded for IP: ${req.ip}`);
      res.status(429).json({
        success: false,
        message: '登录尝试过于频繁，请15分钟后再试'
      });
    }
  }),

  // 文件上传限制
  upload: rateLimit({
    windowMs: 60 * 1000, // 1分钟
    max: 10, // 每个IP每分钟最多10次上传
    message: {
      success: false,
      message: '上传过于频繁，请稍后再试'
    },
    handler: (req, res) => {
      logger.warn(`Upload rate limit exceeded for IP: ${req.ip}`);
      res.status(429).json({
        success: false,
        message: '上传过于频繁，请稍后再试'
      });
    }
  }),

  // 管理员操作限制
  admin: rateLimit({
    windowMs: 5 * 60 * 1000, // 5分钟
    max: 20, // 每个IP最多20个管理员请求
    message: {
      success: false,
      message: '管理员操作过于频繁，请稍后再试'
    },
    handler: (req, res) => {
      logger.warn(`Admin rate limit exceeded for IP: ${req.ip}`);
      res.status(429).json({
        success: false,
        message: '管理员操作过于频繁，请稍后再试'
      });
    }
  })
};

/**
 * 安全头配置
 */
const helmetConfig = helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'", "https://cdn.tailwindcss.com", "https://cdn.jsdelivr.net"],
      scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.tailwindcss.com"],
      imgSrc: ["'self'", "data:", "blob:"],
      fontSrc: ["'self'", "https://cdn.jsdelivr.net"],
      connectSrc: ["'self'"],
      mediaSrc: ["'self'", "blob:"],
      objectSrc: ["'none'"],
      frameSrc: ["'none'"]
    }
  },
  crossOriginEmbedderPolicy: false,
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
});

/**
 * XSS防护中间件
 */
const xssProtection = (req, res, next) => {
  // 清理请求体中的XSS
  if (req.body && typeof req.body === 'object') {
    req.body = sanitizeObject(req.body);
  }

  // 清理查询参数中的XSS
  if (req.query && typeof req.query === 'object') {
    req.query = sanitizeObject(req.query);
  }

  next();
};

/**
 * 递归清理对象中的XSS
 */
function sanitizeObject(obj) {
  if (typeof obj === 'string') {
    return xss(obj, {
      whiteList: {
        // 允许的HTML标签（用于富文本编辑器）
        p: [],
        br: [],
        strong: [],
        em: [],
        u: [],
        h1: [], h2: [], h3: [], h4: [], h5: [], h6: [],
        ul: [], ol: [], li: [],
        blockquote: [],
        a: ['href', 'title'],
        img: ['src', 'alt', 'width', 'height'],
        div: ['class'],
        span: ['class']
      },
      stripIgnoreTag: true,
      stripIgnoreTagBody: ['script', 'style']
    });
  }

  if (Array.isArray(obj)) {
    return obj.map(item => sanitizeObject(item));
  }

  if (obj && typeof obj === 'object') {
    const sanitized = {};
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        sanitized[key] = sanitizeObject(obj[key]);
      }
    }
    return sanitized;
  }

  return obj;
}

/**
 * SQL注入防护中间件
 */
const sqlInjectionProtection = (req, res, next) => {
  const suspiciousPatterns = [
    /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION|SCRIPT)\b)/gi,
    /(\b(OR|AND)\s+\d+\s*=\s*\d+)/gi,
    /(--|\/\*|\*\/|;)/g,
    /(\b(CHAR|NCHAR|VARCHAR|NVARCHAR)\s*\(\s*\d+\s*\))/gi
  ];

  const checkForSqlInjection = (value) => {
    if (typeof value === 'string') {
      return suspiciousPatterns.some(pattern => pattern.test(value));
    }
    return false;
  };

  const checkObject = (obj) => {
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        if (typeof obj[key] === 'string' && checkForSqlInjection(obj[key])) {
          return true;
        }
        if (typeof obj[key] === 'object' && obj[key] !== null) {
          if (checkObject(obj[key])) {
            return true;
          }
        }
      }
    }
    return false;
  };

  // 检查请求体
  if (req.body && checkObject(req.body)) {
    logger.warn(`SQL injection attempt detected from IP: ${req.ip}, Body:`, req.body);
    return res.status(400).json({
      success: false,
      message: '请求包含非法字符'
    });
  }

  // 检查查询参数
  if (req.query && checkObject(req.query)) {
    logger.warn(`SQL injection attempt detected from IP: ${req.ip}, Query:`, req.query);
    return res.status(400).json({
      success: false,
      message: '请求包含非法字符'
    });
  }

  next();
};

/**
 * 输入验证中间件
 */
const inputValidation = {
  // 邮箱验证
  email: (req, res, next) => {
    const { email } = req.body;
    if (email && !validator.isEmail(email)) {
      return res.status(400).json({
        success: false,
        message: '邮箱格式不正确'
      });
    }
    next();
  },

  // 密码强度验证
  password: (req, res, next) => {
    const { password } = req.body;
    if (password) {
      if (password.length < 6) {
        return res.status(400).json({
          success: false,
          message: '密码至少需要6个字符'
        });
      }
      if (password.length > 128) {
        return res.status(400).json({
          success: false,
          message: '密码不能超过128个字符'
        });
      }
    }
    next();
  },

  // 用户名验证
  nickname: (req, res, next) => {
    const { nickname } = req.body;
    if (nickname) {
      if (nickname.length < 1 || nickname.length > 50) {
        return res.status(400).json({
          success: false,
          message: '用户名长度必须在1-50个字符之间'
        });
      }
      if (!/^[a-zA-Z0-9\u4e00-\u9fa5_-]+$/.test(nickname)) {
        return res.status(400).json({
          success: false,
          message: '用户名只能包含字母、数字、中文、下划线和连字符'
        });
      }
    }
    next();
  },

  // 文件路径验证
  filePath: (req, res, next) => {
    const { path: filePath } = req.body;
    if (filePath) {
      // 防止目录遍历攻击
      if (filePath.includes('..') || filePath.includes('~') || path.isAbsolute(filePath)) {
        return res.status(400).json({
          success: false,
          message: '文件路径不合法'
        });
      }
    }
    next();
  }
};

/**
 * 文件上传安全验证
 */
const fileUploadSecurity = {
  // 允许的文件类型
  allowedMimeTypes: [
    // 图片
    'image/jpeg', 'image/png', 'image/webp', 'image/gif',
    // 视频
    'video/mp4', 'video/webm', 'video/avi', 'video/mov',
    // 音频
    'audio/mp3', 'audio/wav', 'audio/ogg', 'audio/m4a',
    // 文档
    'application/pdf', 'text/plain', 'text/csv',
    'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    // 压缩文件
    'application/zip', 'application/x-rar-compressed', 'application/x-7z-compressed'
  ],

  // 危险文件扩展名
  dangerousExtensions: [
    '.exe', '.bat', '.cmd', '.com', '.pif', '.scr', '.vbs', '.js', '.jar',
    '.php', '.asp', '.aspx', '.jsp', '.sh', '.py', '.rb', '.pl'
  ],

  // 文件验证中间件
  validateFile: (req, res, next) => {
    if (!req.file && !req.files) {
      return next();
    }

    const files = req.files || [req.file];
    
    for (const file of files) {
      // 检查文件类型
      if (!fileUploadSecurity.allowedMimeTypes.includes(file.mimetype)) {
        return res.status(400).json({
          success: false,
          message: `不支持的文件类型: ${file.mimetype}`
        });
      }

      // 检查文件扩展名
      const ext = path.extname(file.originalname).toLowerCase();
      if (fileUploadSecurity.dangerousExtensions.includes(ext)) {
        return res.status(400).json({
          success: false,
          message: `不允许上传的文件类型: ${ext}`
        });
      }

      // 检查文件大小
      const maxSize = 100 * 1024 * 1024; // 100MB
      if (file.size > maxSize) {
        return res.status(413).json({
          success: false,
          message: '文件大小超出限制（最大100MB）'
        });
      }

      // 检查文件名
      if (!/^[a-zA-Z0-9\u4e00-\u9fa5._-]+$/.test(file.originalname)) {
        return res.status(400).json({
          success: false,
          message: '文件名包含非法字符'
        });
      }
    }

    next();
  },

  // 文件内容验证（检查文件头）
  validateFileContent: async (req, res, next) => {
    if (!req.file && !req.files) {
      return next();
    }

    const files = req.files || [req.file];

    try {
      for (const file of files) {
        // 读取文件头部字节
        const buffer = await fs.promises.readFile(file.path);
        const header = buffer.slice(0, 16);

        // 验证文件头是否与声明的MIME类型匹配
        if (!validateFileHeader(header, file.mimetype)) {
          // 删除上传的文件
          try {
            await fs.promises.unlink(file.path);
          } catch (err) {
            logger.error('Failed to delete invalid file:', err);
          }

          return res.status(400).json({
            success: false,
            message: '文件内容与文件类型不匹配'
          });
        }
      }
      next();
    } catch (error) {
      logger.error('File content validation error:', error);
      return res.status(500).json({
        success: false,
        message: '文件验证失败'
      });
    }
  }
};

/**
 * 验证文件头
 */
function validateFileHeader(buffer, mimeType) {
  const signatures = {
    'image/jpeg': [0xFF, 0xD8, 0xFF],
    'image/png': [0x89, 0x50, 0x4E, 0x47],
    'image/webp': [0x52, 0x49, 0x46, 0x46],
    'image/gif': [0x47, 0x49, 0x46],
    'video/mp4': [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70],
    'application/pdf': [0x25, 0x50, 0x44, 0x46],
    'application/zip': [0x50, 0x4B, 0x03, 0x04]
  };

  const signature = signatures[mimeType];
  if (!signature) {
    // 如果没有定义签名，允许通过（对于文本文件等）
    return true;
  }

  // 检查文件头是否匹配
  for (let i = 0; i < signature.length; i++) {
    if (buffer[i] !== signature[i]) {
      return false;
    }
  }

  return true;
}

/**
 * CSRF保护中间件
 */
const csrfProtection = (req, res, next) => {
  // 对于GET请求，不需要CSRF保护
  if (req.method === 'GET') {
    return next();
  }

  // 检查CSRF token
  const token = req.headers['x-csrf-token'] || req.body._csrf;
  const sessionToken = req.session?.csrfToken;

  if (!token || !sessionToken || token !== sessionToken) {
    logger.warn(`CSRF token mismatch for IP: ${req.ip}, Path: ${req.path}`);
    return res.status(403).json({
      success: false,
      message: 'CSRF token验证失败'
    });
  }

  next();
};

/**
 * 生成CSRF token
 */
const generateCSRFToken = () => {
  return crypto.randomBytes(32).toString('hex');
};

/**
 * IP白名单中间件
 */
const ipWhitelist = (whitelist = []) => {
  return (req, res, next) => {
    if (whitelist.length === 0) {
      return next();
    }

    const clientIP = req.ip || req.connection.remoteAddress;
    
    if (!whitelist.includes(clientIP)) {
      logger.warn(`Access denied for IP: ${clientIP}`);
      return res.status(403).json({
        success: false,
        message: '访问被拒绝'
      });
    }

    next();
  };
};

/**
 * 请求日志中间件
 */
const requestLogger = (req, res, next) => {
  const start = Date.now();
  
  res.on('finish', () => {
    const duration = Date.now() - start;
    const logData = {
      method: req.method,
      url: req.url,
      ip: req.ip,
      userAgent: req.get('User-Agent'),
      statusCode: res.statusCode,
      duration: `${duration}ms`
    };

    if (res.statusCode >= 400) {
      logger.warn('Request failed:', logData);
    } else {
      logger.info('Request completed:', logData);
    }
  });

  next();
};

module.exports = {
  rateLimitConfig,
  helmetConfig,
  xssProtection,
  sqlInjectionProtection,
  inputValidation,
  fileUploadSecurity,
  csrfProtection,
  generateCSRFToken,
  ipWhitelist,
  requestLogger
};